/*
 * Copyright (c) 2015 LingoChamp Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.liulishuo.magicprogresswidget;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.SweepShader;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.global.icu.math.BigDecimal;

/**
 * MagValueAnimator
 *
 * @since 2012/08/15
 * Created by Jacksgong on 12/8/15.
 * 初始值和结束值动画
 */
public class MagicProgressCircle extends Component implements Imag, Component.DrawTask, Component.EstimateSizeListener {
    private static final String MPC_PERCENT = "mpc_percent";
    private static final String MPC_STROKE_WIDTH = "mpc_stroke_width";
    private static final String MPC_START_COLOR = "mpc_start_color";
    private static final String MPC_END_COLOR = "mpc_end_color";
    private static final String MPC_DEFAULT_COLOR = "mpc_default_color";
    private static final String MPC_FOOT_OVER_HEAD = "mpc_foot_over_head";

    private int startColor = ConstantUtil.START_COLOR;
    private int endColor = ConstantUtil.END_COLOR;
    private int defaultColor = ConstantUtil.DEFAULT_COLOR;
    private int percentEndColor;
    private int strokeWidth;
    private float magPc;
    private float endPc;
    private MagValueAnimator magVa;
    private long maxTime = ConstantUtil.MAX_TIME;
    private Paint paint;
    private boolean isFootOverHead;
    private Paint startPaint;
    private Paint endPaint;
    private final RectFloat rectF = new RectFloat();
    private Color[] customColors;
    private Color[] cvsColors;
    private Color[] emptyColors;
    private float[] magPositions;
    private float[] stPositions;
    private int deltaR;
    private int deltaB;
    private int deltaG;
    private int startR;
    private int startB;
    private int startG;

    /**
     * MagicProgressCircle
     *
     * @param context 上下文
     */
    public MagicProgressCircle(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * MagicProgressCircle
     *
     * @param context 上下文
     * @param attrs   attr
     */
    public MagicProgressCircle(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * MagicProgressCircle
     *
     * @param context   上下文
     * @param attrs     attr
     * @param styleName styleName
     */
    public MagicProgressCircle(Context context, AttrSet attrs, String styleName) {
        super(context, attrs, styleName);
        init(context, attrs);
    }

    /**
     * MagicProgressCircle
     *
     * @param context 上下文
     * @param attrs   attr
     * @param resId   styleName
     */
    public MagicProgressCircle(Context context, AttrSet attrs, int resId) {
        super(context, attrs, resId);
        init(context, attrs);
    }

    private void init(final Context context, final AttrSet attrs) {
        setInit(context, attrs);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(strokeWidth);
        paint.setStyle(Paint.Style.STROKE_STYLE);
        paint.setStrokeJoin(Paint.Join.ROUND_JOIN);
        paint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        startPaint = new Paint();
        startPaint.setColor(new Color(startColor));
        startPaint.setAntiAlias(true);
        startPaint.setStyle(Paint.Style.FILL_STYLE);
        endPaint = new Paint();
        endPaint.setAntiAlias(true);
        endPaint.setStyle(Paint.Style.FILL_STYLE);
        refreshDelta();
        customColors = new Color[]{new Color(startColor), new Color(percentEndColor), new Color(defaultColor), new Color(defaultColor)};
        cvsColors = new Color[]{new Color(startColor), new Color(endColor)};
        emptyColors = new Color[]{new Color(defaultColor), new Color(defaultColor)};
        magPositions = new float[4];
        magPositions[0] = 0;
        magPositions[3] = 1;
        stPositions = new float[]{0, 1};
        addDrawTask(this);
        setEstimateSizeListener(this);
    }

    /**
     * setInit
     *
     * @param context 上下文
     * @param attrs   atr
     */
    private void setInit(Context context, AttrSet attrs) {
        float defaultPercent = 0;
        final int strokeWdithDefaultValue = (int) (vpToPx(getContext(), 18) + 0.5f);
        if (context == null || attrs == null) {
            magPc = defaultPercent;
            strokeWidth = strokeWdithDefaultValue;
        } else {
            Util util = new Util(attrs);
            magPc = util.getFloatValue(MPC_PERCENT, defaultPercent);
            strokeWidth = util.getDimensionValue(MPC_STROKE_WIDTH, strokeWdithDefaultValue);
            startColor = util.getColorValue(MPC_START_COLOR, startColor);
            endColor = util.getColorValue(MPC_END_COLOR, endColor);
            defaultColor = util.getColorValue(MPC_DEFAULT_COLOR, defaultColor);
            isFootOverHead = util.getBooleanValue(MPC_FOOT_OVER_HEAD, false);
        }
    }

    private void refreshDelta() {
        int endR = (endColor & ConstantUtil.END_R) >> 16;
        int endG = (endColor & ConstantUtil.END_G) >> 8;
        int endB = (endColor & ConstantUtil.END_B);
        startR = (startColor & ConstantUtil.END_R) >> 16;
        startG = (startColor & ConstantUtil.END_G) >> 8;
        startB = (startColor & ConstantUtil.END_B);
        deltaR = endR - startR;
        deltaG = endG - startG;
        deltaB = endB - startB;
    }

    @Override
    public float getPercent() {
        return this.magPc;
    }

    @Override
    public void setPercent(float percent) {
        percent = cirPt(percent);
        float pc = pcInt(percent);
        anim(0, pc, (long) (pc * maxTime));
    }

    @Override
    public void setPercent(float percent, long durationMillis) {
        percent = cirPt(percent);
        float pc = pcInt(percent);
        anim(0, pc, durationMillis);
    }

    @Override
    public void setSmoothPercent(float percent) {
        percent = cirPt(percent);
        float pc = pcInt(percent);
        anim(endPc, pc, (long) ((pc - endPc) * maxTime));
    }

    @Override
    public void setSmoothPercent(float percent, long durationMillis) {
        percent = cirPt(percent);
        float pc = pcInt(percent);
        anim(endPc, pc, durationMillis);
    }

    /**
     * cirPt 这里动画效果在值位于98-99时处于覆盖情况，做特殊处理
     *
     * @param cpt 传递的圈参数
     * @return cpt 处理值
     */
    private float cirPt(float cpt) {
        if (cpt > ConstantUtil.CIRCLE_97 / ConstantUtil.CIR_MAX && cpt < 1) {
            cpt = ConstantUtil.CIRCLE_97 / ConstantUtil.CIR_MAX;
        }
        return cpt;
    }

    private void anim(float lastPercent, float percent, long duration) {
        if (magVa != null) {
            if (magVa.isRunning()) {
                magVa.end();
            }
            magVa.release();
        }
        magVa = MagValueAnimator.ofFloat(lastPercent, percent);
        magVa.setDuration(duration);
        magVa.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                MagicProgressCircle.this.magPc = value;
                invalidate();
            }
        });
        magVa.start();
        this.endPc = percent;
    }

    /**
     * 设置渐变颜色起点颜色(percent=0)
     *
     * @param color ColorInt，argb
     */
    public void setStartColor(final int color) {
        if (this.startColor != color) {
            this.startColor = color;
            /**
             * elta变化
             */
            refreshDelta();
            /**
             * 渐变前部分
             */
            customColors[0] = new Color(color);
            /**
             * 前半圆
             */
            startPaint.setColor(new Color(color));
            /**
             * 全满时 渐变起点
             */
            cvsColors[0] = new Color(color);
            invalidate();
        }
    }

    /**
     * 设置未填充部分的描边的颜色
     *
     * @param color ColorInt，argb
     */
    public void setDefaultColor(final int color) {
        if (this.defaultColor != color) {
            this.defaultColor = color;
            customColors[2] = new Color(color);
            customColors[3] = new Color(color);
            emptyColors[0] = new Color(color);
            emptyColors[1] = new Color(color);
            invalidate();
        }
    }

    /**
     * 设置渐变颜色终点颜色(percent=1)
     *
     * @param color ColorInt，argb
     */
    public void setEndColor(final int color) {
        if (this.endColor != color) {
            this.endColor = color;
            refreshDelta();
            cvsColors[1] = new Color(color);
            invalidate();
        }
    }

    public int getEndColor() {
        return this.endColor;
    }

    public int getDefaultColor() {
        return this.defaultColor;
    }

    /**
     * 设置描边宽度
     *
     * @param width px
     */
    public void setStrokeWidth(final int width) {
        if (this.strokeWidth != width) {
            this.strokeWidth = width;
            paint.setStrokeWidth(width);
            invalidate();
        }
    }

    public int getStrokeWidth() {
        return this.strokeWidth;
    }

    /**
     * 设置结尾的圆弧是否覆盖开始的圆弧
     *
     * @param footOverHead Boolean
     */
    public void setFootOverHead(boolean footOverHead) {
        if (this.isFootOverHead != footOverHead) {
            this.isFootOverHead = footOverHead;
            invalidate();
        }
    }

    public boolean isFootOverHead() {
        return isFootOverHead;
    }

    private void calculatePercentEndColor(final float percent) {
        percentEndColor = ((int) (deltaR * percent + startR) << 16) +
                ((int) (deltaG * percent + startG) << 8) +
                ((int) (deltaB * percent + startB)) + 0xFF000000;
    }

    @Override
    public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
        this.rectF.left = EstimateSpec.getSize(widthEstimateConfig) / 2 - strokeWidth / 2;
        this.rectF.top = 0;
        this.rectF.right = EstimateSpec.getSize(widthEstimateConfig) / 2 + strokeWidth / 2;
        this.rectF.bottom = strokeWidth;
        return false;
    }

    private float pcInt(float percent) {
        float pc = Math.max(0, percent);
        return pc;
    }

    /**
     * onDraw
     *
     * @param component
     * @param canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        final int restore = canvas.save();
        final int width = getWidth();
        final int height = getHeight();
        final int cx = width / 2;
        final int cy = height / 2;
        final int radius = width / 2 - strokeWidth / 2;
        float drawPc = magPc;
        canvas.save();
        canvas.rotate(-90, cx, cy);
        Color[] colors;
        float[] positions;
        if (drawPc < 1 && drawPc > 0) {
            calculatePercentEndColor(drawPc);
            customColors[1] = new Color(percentEndColor);
            colors = customColors;
            magPositions[1] = drawPc;
            magPositions[2] = drawPc;
            positions = magPositions;
        } else if (drawPc == 1) {
            percentEndColor = endColor;
            colors = cvsColors;
            positions = stPositions;
        } else {
            colors = emptyColors;
            positions = stPositions;
        }
        SweepShader sweepGradient = new SweepShader(cx, cy, colors, positions);
        paint.setShader(sweepGradient, Paint.ShaderType.SWEEP_SHADER);
        canvas.drawCircle(cx, cy, radius, paint);
        canvas.restore();
        if (drawPc > 0) {
            setDrawPc(canvas, drawPc, cx, cy);
        }
        canvas.restoreToCount(restore);
    }

    /**
     * 绘制圆环状态
     *
     * @param canvas 设置cvs
     * @param drawPc 填入值
     * @param cx     旋转x值
     * @param cy     旋转y值
     */
    private void setDrawPc(Canvas canvas, float drawPc, int cx, int cy) {
        if (drawPc < 1 || (isFootOverHead && drawPc == 1)) {
            canvas.save();
            endPaint.setColor(new Color(percentEndColor));
            canvas.rotate(bigDecimal((360.0f * drawPc) - 1), cx, cy);
            canvas.drawArc(rectF, new Arc(-90f, 180f, true), endPaint);
            canvas.restore();
        }
        if (!isFootOverHead || drawPc < 1) {
            canvas.save();
            canvas.drawArc(rectF, new Arc(90f, 180f, true), startPaint);
            canvas.restore();
        }
    }

    /**
     * bigDecimal
     *
     * @param value double
     * @return itg 返回int
     */
    public int bigDecimal(double value) {
        BigDecimal bd = new BigDecimal(value);
        int itg = bd.setScale(0, BigDecimal.ROUND_UP).intValue();
        return itg;
    }

    /**
     * vpToPx
     *
     * @param context ctx
     * @param vp      单位
     * @return ctx计算值
     */
    private float vpToPx(Context context, float vp) {
        return context.getResourceManager().getDeviceCapability().screenDensity / 160 * vp;
    }
}
